<?php

class Llib_Xml extends ErrorLogXml {

    private $_file_body;
    private $_simple_xml_object;
    private $_options = array(
        'ignore_cad_num' => false
    );

    public function __construct($filename = null, array $options = null) {
        parent::__construct();

        if (is_array($filename)) {
            $this->_options = array_merge($this->_options, $filename);
        } else {
            if (isset($options)) {
                $this->_options = array_merge($this->_options, $options);
            }

            if ($filename) {
                if (file_exists($filename)) {
                    $this->loadFromString(file_get_contents($filename));
                } else {
                    $this->logError("Файл", "Файл не знайдено", $filename, "Критична");
                }
            }
        }
    }
    
    public function getSimpleXmlObj(){
    	if(is_object($this->_simple_xml_object))
    		return $this->_simple_xml_object;
    } 
    

    /**
     * Объединяет не пустые элементы массива в строку
     * @param string $glue
     * @param array $array
     * @return string Возвращает строку, содержащую строкое представление всех не пустых элементов массива в указанном порядке, со строкой glue между каждым элементом.
     */
    private function arrayImplode($glue, array $array) {
        $result = array();

        foreach ($array as $value) {
            $value = trim($value);
            if (empty($value))
                continue;
            $result[] = $value;
        }

        return trim(implode($glue, $result));
    }

    /**
     * Формує блок додаткової інформації за наявності такого блоку
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getAdditionalInfoBlock(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $additional_info = array();

            foreach ($path as $block) {
                foreach ($block->AdditionalInfo as $info) {
                    $additional_info[] = (string) $info;
                }
            }

            if (count($additional_info)) {
                $result = array('AdditionalInfo' => $additional_info);
            }
        }

        return $result;
    }

    /**
     * Комплексний тип "Адреса"
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getAddressType(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['Country'] = (string) $path->Country;

            if (!empty($path->ZIP)) {
                $result['ZIP'] = (string) $path->ZIP;
            }

            $result['Region'] = (string) $path->Region;
            $result['District'] = (string) $path->District;
            $result['Settlement'] = (string) $path->Settlement;
            $result['Street'] = (string) $path->Street;
            $result['Building'] = (string) $path->Building;

            if (!empty($path->Block)) {
                $result['Block'] = (string) $path->Block;
            }

            if (!empty($path->BuildingUnit)) {
                $result['BuildingUnit'] = preg_match("/^\d*$/u", (string) $path->BuildingUnit) ? (string) $path->BuildingUnit : null;
            }

            $result['FullAddress'] = $this->arrayImplode(", ", $result);
        }

        return $result;
    }

    /**
     * Формує кадастровий номер
     * @return array
     */
    public function getCadastralNumber() {
        $result = array('KOATUU' => "", 'CadastralZoneNumber' => "", 'CadastralQuarterNumber' => "", 'ParcelID' => "", 'CadastralNumber' => "");

        if (!$this->hasErrors()) {
            $cad_zone = $this->_simple_xml_object->InfoPart->CadastralZoneInfo;

            $result['KOATUU'] = (string) $cad_zone->KOATUU;
            $result['CadastralZoneNumber'] = (string) $cad_zone->CadastralZoneNumber;
            $result['CadastralQuarterNumber'] = (string) $cad_zone->CadastralQuarters->CadastralQuarterInfo->CadastralQuarterNumber;
            $result['ParcelID'] = (string) $cad_zone->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->ParcelMetricInfo->ParcelID;
            $result['CadastralNumber'] = $this->arrayImplode(":", $result);
        }

        return $result;
    }

    /**
     * Кадастровий квартал
     * @return array
     */
    public function getCadastralQuarter() {
        $result = array();

        if (!$this->hasErrors()) {
            $cad_quarter = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo;

            $result['CadastralQuarterNumber'] = (string) $cad_quarter->CadastralQuarterNumber;

            if (!empty($cad_quarter->RegionalContacts)) {
                $result['RegionalContacts']['LocalAuthorityHead'] = $this->getFullNameType($cad_quarter->RegionalContacts->LocalAuthorityHead);
                $result['RegionalContacts']['DKZRHead'] = $this->getFullNameType($cad_quarter->RegionalContacts->DKZRHead);
            }
        }

        return $result;
    }

    /**
     * Кадастрова зона
     * @return array
     */
    public function getCadastralZone() {
        $result = array();

        if (!$this->hasErrors()) {
            $cad_zone = $this->_simple_xml_object->InfoPart->CadastralZoneInfo;

            $result['KOATUU'] = (string) $cad_zone->KOATUU;
            $result['CadastralZoneNumber'] = (string) $cad_zone->CadastralZoneNumber;
        }

        return $result;
    }

    /**
     *
     * @return array
     */
    public function getExchangeFileData() {
        $result = array();

        if (!$this->hasErrors()) {
            $result['CadastralNumber'] = $this->getCadastralNumber();
            $result['MetricInfo'] = $this->getMetricInfo();
            $result['ServiceInfo'] = $this->getServiceInfo();
            $result['InfoLandWork'] = $this->getInfoLandWork();
            $result['CadastralZoneInfo'] = $this->getCadastralZone();
            $result['CadastralQuarterInfo'] = $this->getCadastralQuarter();
            $result['ParcelInfo'] = $this->getParcelInfo();
        }

        return $result;
    }

    /**
     * Комплексний тип "Відомості про виконавця робіт"
     * @param SimpleXMLElement $path
     * @return array
     */
    public function getExecutorType(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['CompanyName'] = (string) $path->CompanyName;

            if (!empty($path->EDRPOU)) {
                // Виконавець робіт - юридична особа
                $result['EDRPOU'] = (string) $path->EDRPOU;
            } elseif (!empty($path->TaxNumber)) {
                // Виконавець робіт - фізична особа
                $result['TaxNumber'] = (string) $path->TaxNumber;
            }

            $result = array_merge($result, $this->getExecutorLicense($path->License));
            $result = array_merge($result, $this->getExecutorChief($path->Chief));
            $result = array_merge($result, $this->getExecutorExecutor($path->Executor));
            $result = array_merge($result, array('Address' => $this->getAddressType($path->Address)));
        }

        return $result;
    }

    /**
     * Інформація про ліцензії на право проведення робіт
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getExecutorLicense(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['LicenseSeries'] = (string) $path->LicenseSeries;
            $result['LicenseNumber'] = (string) $path->LicenseNumber;
            $result = array_merge($result, $this->makeDate($path->LicenseIssuedDate, 'LicenseIssuedDate', "d.m.Y"));
        }

        return array('License' => $result);
    }

    /**
     * Дані про відповідальну особу
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getExecutorChief(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['ChiefName'] = $this->getFullNameType($path->ChiefName);
            $result['ChiefPosition'] = (string) $path->ChiefPosition;
        }

        return array('Chief' => $result);
    }

    /**
     * Дані про безпосереднього виконавця
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getExecutorExecutor(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['ExecutorName'] = $this->getFullNameType($path->ExecutorName);
            $result['ExecutorPosition'] = (string) $path->ExecutorPosition;

            if (!empty($path->ContactInfo)) {

                foreach ($path->ContactInfo->Phone as $value) {
                    $result['ContactInfo']['Phone'][] = (string) $value;
                }

                foreach ($path->ContactInfo->Fax as $value) {
                    $result['ContactInfo']['Fax'][] = (string) $value;
                }

                foreach ($path->ContactInfo->Email as $value) {
                    $result['ContactInfo']['Email'][] = (string) $value;
                }
            }
        }

        return array('Executor' => $result);
    }

    /**
     * Возвращает тело обменного файла
     * @return string
     */
    public function getFileBody() {
        return $this->_file_body;
    }

    /**
     * Комплексний тип "Прізвище, ім'я, по батькові"
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getFullNameType(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['LastName'] = (string) $path->LastName;
            $result['FirstName'] = (string) $path->FirstName;

            if (!empty($path->MiddleName)) {
                $result['MiddleName'] = (string) $path->MiddleName;
            }

            $result['FullName'] = $this->arrayImplode(" ", $result);
        }

        return $result;
    }

    /**
     * Інформація про осіб, які сформували, здійснили перевірку (коригування) даних обмінного файлу
     * @return array
     */
    public function getInfoLandWork() {
        $result = array();

        if (!$this->hasErrors()) {
            $info_land_work = $this->_simple_xml_object->AdditionalPart->InfoLandWork;

            $result = array_merge($result, array('Executor' => $this->getExecutorType($info_land_work->Executor)));
            $result = array_merge($result, $this->getInfoLandWorkActions($info_land_work->Actions));
        }

        return $result;
    }

    /**
     * Опис перевірок або коригувань даних обмінного файлу
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getInfoLandWorkActions(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $actions = array();

            foreach ($path as $action) {
                foreach ($action->TypeAction as $type_action) {
                    $current_action = array();

                    $current_action['Action'] = (string) $type_action->Action;
                    $current_action['ExecutorName'] = $this->getFullNameType($type_action->ExecutorName);
                    $current_action['ExecutorPosition'] = (string) $type_action->ExecutorPosition;
                    $current_action = array_merge($current_action, $this->makeDate($type_action->DateApproved, 'DateApproved', "d.m.Y"));

                    $actions[] = $current_action;
                }
            }

            if (!empty($actions)) {
                $result = array('Actions' => $actions);
            }
        }

        return $result;
    }

    /**
     * Комплексний тип "Дані про юридичну особу"
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getLegalEntityType(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['Name'] = (string) $path->Name;
            $result['EDRPOU'] = (string) $path->EDRPOU;
            $result = array_merge($result, array('Address' => $this->getAddressType($path->Address)));
            $result = array_merge($result, $this->getAdditionalInfoBlock($path->AdditionalInfoBlock));
        }

        return $result;
    }

    /**
     * Метрична інформація обмінного файлу
     * @return array
     */
    public function getMetricInfo() {
        $result = array();

        if (!$this->hasErrors()) {
            $metric_info = $this->_simple_xml_object->InfoPart->MetricInfo;

            if (isset($metric_info->CoordinateSystem->SC42)) {
                $result['CoordinateSystem'] = "SC42";
            } elseif (isset($metric_info->CoordinateSystem->SC42_3)) {
                $result['CoordinateSystem'] = "SC42_3";
            } elseif (isset($metric_info->CoordinateSystem->Local)) {
                $result['CoordinateSystem'] = "Local";
            } elseif (isset($metric_info->CoordinateSystem->SC63)) {
                $result['CoordinateSystem'] = "SC63";
            } else {
                $result['CoordinateSystem'] = null;
            }

            if (!empty($metric_info->PointInfo->Point->Y)) {
                if (preg_match("/^(\d)\d{6}(\.|,)\d*$/u", (string) $metric_info->PointInfo->Point->Y, $matches)) {
                    $result['CoordinateSystemZone'] = $matches[1];
                } else {
                    $result['CoordinateSystemZone'] = null;
                }
            } else {
                $result['CoordinateSystemZone'] = null;
            }
        }

        return $result;
    }

    /**
     * Комплексний тип "Дані про фізичну особу"
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getNaturalPersonType(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['FullName'] = $this->getFullNameType($path->FullName);

            if (!empty($path->TaxNumber)) {
                $result['TaxNumber'] = (string) $path->TaxNumber;
            }

            $result = array_merge($result, $this->getNaturalPersonPassport($path->Passport));

            if (!empty($path->Citizenship)) {
                $result['Citizenship'] = (string) $path->Citizenship;
            }

            $result['Address'] = $this->getAddressType($path->Address);
            $result = array_merge($result, $this->getAdditionalInfoBlock($path->AdditionalInfoBlock));
        }

        return $result;
    }

    /**
     * Документ, що посвідчує фізичну особу
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getNaturalPersonPassport(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $result['DocumentType'] = (string) $path->DocumentType;
            $result['PassportNumber'] = (string) $path->PassportNumber;
            $result = array_merge($result, $this->makeDate($path->PassportIssuedDate, 'PassportIssuedDate', "d.m.Y"));
            $result['IssuanceAuthority'] = (string) $path->IssuanceAuthority;
            $result['PassportSeries'] = (string) $path->PassportSeries;
            $result['FullPassportText'] = $this->arrayImplode(", ", array($path->DocumentType, $result['PassportSeries'] . " " . $result['PassportNumber']));
        }

        return array('Passport' => $result);
    }

    /**
     * Земельна ділянка
     * @return array
     */
    public function getParcelInfo() {
        $result = array();

        if (!$this->hasErrors()) {
            $parcel_info = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo;

            $result = array_merge($result, $this->getParcelLocationInfo($parcel_info->ParcelLocationInfo));
            $result = array_merge($result, $this->getParcelCategoryPurposeInfo($parcel_info->CategoryPurposeInfo));
            $result = array_merge($result, $this->getParcelMetricInfo($parcel_info->ParcelMetricInfo));
            $result['OwnershipInfo'] = (string) $parcel_info->OwnershipInfo->Code;
            $result = array_merge($result, $this->getParcelProprietors($parcel_info->Proprietors));
            $result = array_merge($result, $this->getParcelLegalModeInfo($parcel_info->LegalModeInfo));
            $result = array_merge($result, $this->getParcelTechnicalDocumentation($parcel_info->TechnicalDocumentationInfo));
            $result = array_merge($result, $this->getParcelStateActInfo($parcel_info->StateActInfo));
            $result = array_merge($result, $this->getParcelValuationInfo($parcel_info->ValuationInfo));
            $result = array_merge($result, $this->getParcelLeases($parcel_info->Leases));
            $result = array_merge($result, $this->getParcelSubleases($parcel_info->Subleases));
            $result = array_merge($result, $this->getParcelRestrictions($parcel_info->Restrictions));
            $result = array_merge($result, $this->getParcelLands($parcel_info->LandsParcel));
            $result = array_merge($result, $this->getParcelAdjacent($parcel_info->AdjacentUnits));
        }

        return $result;
    }

    /**
     * Суміжники кадастрової одиниці
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelAdjacent(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $adjacent_info = array();

            foreach ($path->AdjacentUnitInfo as $adjacent) {
                $current_adjacent = array();

                $current_adjacent['CadastralNumber'] = (string) $adjacent->CadastralNumber;

                if (isset($adjacent->Proprietor->NaturalPerson)) {
                    $current_adjacent['Proprietor']['NaturalPerson'] = $this->getNaturalPersonType($adjacent->Proprietor->NaturalPerson);
                } elseif (isset($adjacent->Proprietor->LegalEntity)) {
                    $current_adjacent['Proprietor']['LegalEntity'] = $this->getLegalEntityType($adjacent->Proprietor->LegalEntity);
                }

                $current_adjacent = array_merge($current_adjacent, $this->getAdditionalInfoBlock($adjacent->AdditionalInfoBlock));

                $adjacent_info[] = $current_adjacent;
            }

            if (count($adjacent_info)) {
                $result = array('AdjacentUnits' => $adjacent_info);
            }
        }

        return $result;
    }

    /**
     * Категорія та цільове призначення (використання) земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelCategoryPurposeInfo(SimpleXMLElement $path) {
        $result = array();

        $result['Category'] = (string) $path->Category;
        $result['Purpose'] = (string) $path->Purpose;
        $result['Use'] = (string) $path->Use;

        return array('CategoryPurposeInfo' => $result);
    }

    /**
     * Оренда земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    public function getParcelLeases(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $leases_info = array();
            $break = false;

            foreach ($path->LeaseInfo as $lease) {
                $current_lease = array();

                if (!empty($lease->ExecutivePowerDecision)) {
                    $current_lease['ExecutivePowerDecision']['ExecutivePower'] = (string) $lease->ExecutivePowerDecision->ExecutivePower;
                    $current_lease['ExecutivePowerDecision']['ApprovalDocument'] = (string) $lease->ExecutivePowerDecision->ApprovalDocument;
                    $current_lease['ExecutivePowerDecision']['ApprovalDocumentName'] = (string) $lease->ExecutivePowerDecision->ApprovalDocumentName;
                    $current_lease['ExecutivePowerDecision'] = array_merge($current_lease['ExecutivePowerDecision'], $this->makeDate($lease->ExecutivePowerDecision->ApprovalDate, 'ApprovalDate', "d.m.Y"));
                    $current_lease['ExecutivePowerDecision']['ApprovalDocumentNumber'] = (string) $lease->ExecutivePowerDecision->ApprovalDocumentNumber;
                }

                if (!empty($lease->LeaseAgreement)) {

                    if (isset($lease->LeaseAgreement->Leasees)) {
                        if ($lease->LeaseAgreement->Leasees->Leasee->count() > 1) {
                            foreach ($lease->LeaseAgreement->Leasees->Leasee as $leasee) {
                                if (isset($leasee->NaturalPerson)) {
                                    if ($this->isValidNaturalPerson($leasee->NaturalPerson))
                                        $current_lease['LeaseAgreement']['Leasee'][]['NaturalPerson'] = $this->getNaturalPersonType($leasee->NaturalPerson);
                                } elseif (isset($leasee->LegalEntity)) {
                                    if ($this->isValidLegalEntity($leasee->LegalEntity))
                                        $current_lease['LeaseAgreement']['Leasee'][]['LegalEntity'] = $this->getLegalEntityType($leasee->LegalEntity);
                                }
                            }
                        } else {
                            $get_from_grantee = false;

                            if (isset($lease->LeaseAgreement->Leasees->Leasee->NaturalPerson)) {
                                if ($this->isValidNaturalPerson($lease->LeaseAgreement->Leasees->Leasee->NaturalPerson)) {
                                    $current_lease['LeaseAgreement']['Leasee'][]['NaturalPerson'] = $this->getNaturalPersonType($lease->LeaseAgreement->Leasees->Leasee->NaturalPerson);
                                } else {
                                    $get_from_grantee = true;
                                }
                            } elseif (isset($lease->LeaseAgreement->Leasees->Leasee->LegalEntity)) {
                                if ($this->isValidLegalEntity($lease->LeaseAgreement->Leasees->Leasee->LegalEntity)) {
                                    $current_lease['LeaseAgreement']['Leasee'][]['LegalEntity'] = $this->getLegalEntityType($lease->LeaseAgreement->Leasees->Leasee->LegalEntity);
                                } else {
                                    $get_from_grantee = true;
                                }
                            }

                            if ($get_from_grantee) {
                                $grantee = $this->getGrantee(2);

                                foreach ($grantee as $value) {
                                    if (isset($value->NaturalPerson)) {
                                        if ($this->isValidNaturalPerson($value->NaturalPerson))
                                            $current_lease['LeaseAgreement']['Leasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                                    } elseif (isset($value->LegalEntity)) {
                                        if ($this->isValidLegalEntity($value->LegalEntity))
                                            $current_lease['LeaseAgreement']['Leasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                                    }
                                }
                            }
                        }
                    }

                    $current_lease['LeaseAgreement']['Area'] = (string) $lease->LeaseAgreement->Area;

                    if (isset($lease->LeaseAgreement->LeaseTerm)) {
                        $current_lease['LeaseAgreement']['LeaseTerm']['LeaseDuration'] = (string) $lease->LeaseAgreement->LeaseTerm->LeaseDuration;
                        $current_lease['LeaseAgreement']['LeaseTerm'] = array_merge($current_lease['LeaseAgreement']['LeaseTerm'], $this->makeDate($lease->LeaseAgreement->LeaseTerm->StartDate, 'StartDate', "d.m.Y"));
                        $current_lease['LeaseAgreement']['LeaseTerm'] = array_merge($current_lease['LeaseAgreement']['LeaseTerm'], $this->makeDate($lease->LeaseAgreement->LeaseTerm->ExpirationDate, 'ExpirationDate', "d.m.Y"));
                    }

                    if (isset($lease->LeaseAgreement->Rent)) {
                        if (!empty($lease->LeaseAgreement->Rent->MoneyRent)) {
                            $current_lease['LeaseAgreement']['Rent']['MoneyRent'] = (string) $lease->LeaseAgreement->Rent->MoneyRent;
                        } elseif (!empty($lease->LeaseAgreement->Rent->OtherRent)) {
                            $current_lease['LeaseAgreement']['Rent']['OtherRent'] = (string) $lease->LeaseAgreement->Rent->OtherRent;
                        }
                    }

                    $current_lease['LeaseAgreement']['RegistrationNumber'] = (string) $lease->LeaseAgreement->RegistrationNumber;
                    $current_lease['LeaseAgreement'] = array_merge($current_lease['LeaseAgreement'], $this->makeDate($lease->LeaseAgreement->RegistrationDate, 'RegistrationDate', "d.m.Y"));
                } else {
                    $grantee = $this->getGrantee(2);

                    foreach ($grantee as $value) {
                        if (isset($value->NaturalPerson)) {
                            if ($this->isValidNaturalPerson($value->NaturalPerson))
                                $current_lease['LeaseAgreement']['Leasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                        } elseif (isset($value->LegalEntity)) {
                            if ($this->isValidLegalEntity($value->LegalEntity))
                                $current_lease['LeaseAgreement']['Leasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                        }
                    }

                    $break = true;
                }

                if (!empty($current_lease))
                    $leases_info[] = $current_lease;

                if ($break)
                    break;
            }

            if (count($leases_info)) {
                $result = array('Leases' => $leases_info);
            }
        } else {
            $leases_info = array();
            $grantee = $this->getGrantee(2);

            if (count($grantee)) {
                foreach ($grantee as $value) {
                    $current_lease = array();

                    if (isset($value->NaturalPerson)) {
                        if ($this->isValidNaturalPerson($value->NaturalPerson))
                            $current_lease['LeaseAgreement']['Leasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                    } elseif (isset($value->LegalEntity)) {
                        if ($this->isValidLegalEntity($value->LegalEntity))
                            $current_lease['LeaseAgreement']['Leasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                    }

                    if (!empty($current_lease))
                        $leases_info[] = $current_lease;
                }
            }

            if (count($leases_info)) {
                $result = array('Leases' => $leases_info);
            }
        }

        return $result;
    }

    /**
     * Угіддя земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelLands(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $land_info = array();

            foreach ($path->LandParcelInfo as $land) {
                $current_land = array();

                $current_land['CadastralCode'] = (string) $land->CadastralCode;
                $current_land['LandCode'] = (string) $land->LandCode;

                if (!empty($land->MetricInfo)) {
                    if (!empty($land->MetricInfo->Area)) {
                        $current_land['MetricInfo']['Area']['MeasurementUnit'] = (string) $land->MetricInfo->Area->MeasurementUnit;
                        $current_land['MetricInfo']['Area']['Size'] = (string) $land->MetricInfo->Area->Size;

                        if (isset($land->MetricInfo->Area->DeterminationMethod->ExhangeFileCoordinates)) {
                            $current_land['MetricInfo']['Area']['DeterminationMethod'] = "ExhangeFileCoordinates";
                        } elseif (isset($land->MetricInfo->Area->DeterminationMethod->Calculation)) {
                            $current_land['MetricInfo']['Area']['DeterminationMethod'] = "Calculation";
                        } elseif ($land->MetricInfo->Area->DeterminationMethod->DocExch) {
                            $current_land['MetricInfo']['Area']['DeterminationMethod'] = "DocExch";
                        }
                    }

                    $current_land['MetricInfo']['Perimeter'] = (string) $land->MetricInfo->Perimeter;
                }

                $current_land['Error'] = (string) $land->Error;

                $land_info[] = $current_land;
            }

            if (count($land_info)) {
                $result = array('LandsParcel' => $land_info);
            }
        }

        return $result;
    }

    /**
     * Права користування земельною ділянкою
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelLegalModeInfo(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $legal_mode_info = array();

            foreach ($path as $legal_mode) {
                $current_legal_mode = array();

                $current_legal_mode['LegalModeType'] = preg_match("/^\d{1}$/u", (string) $legal_mode->LegalModeType) ? (string) $legal_mode->LegalModeType : null;

                if (!empty($legal_mode->Duration)) {
                    $current_legal_mode = array_merge($current_legal_mode, $this->makeDate($legal_mode->Duration->StartDate, 'StartDate', "d.m.Y"));
                    $current_legal_mode = array_merge($current_legal_mode, $this->makeDate($legal_mode->Duration->ExpirationDate, 'ExpirationDate', "d.m.Y"));
                }

                if (!empty($legal_mode->Grantee)) {
                    $grantees = array();

                    foreach ($legal_mode->Grantee as $grantee) {
                        if (isset($grantee->NaturalPerson)) {
                            $grantees[]['NaturalPerson'] = $this->getNaturalPersonType($grantee->NaturalPerson);
                        } elseif (isset($grantee->LegalEntity)) {
                            $grantees[]['LegalEntity'] = $this->getLegalEntityType($grantee->LegalEntity);
                        }
                    }

                    if (count($grantees)) {
                        $current_legal_mode['Grantee'] = $grantees;
                    }
                }

                if (isset($legal_mode->Grantor->NaturalPerson)) {
                    $current_legal_mode['Grantor']['NaturalPerson'] = $this->getNaturalPersonType($legal_mode->Grantor->NaturalPerson);
                } elseif (isset($legal_mode->Grantor->LegalEntity)) {
                    $current_legal_mode['Grantor']['LegalEntity'] = $this->getLegalEntityType($legal_mode->Grantor->LegalEntity);
                }

                $legal_mode_info[] = $current_legal_mode;
            }

            if (count($legal_mode_info)) {
                $result = array('LegalModeInfo' => $legal_mode_info);
            }
        }

        return $result;
    }

    /**
     * Місцезнаходження земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelLocationInfo(SimpleXMLElement $path) {
        $result = array();

        $result['Region'] = (string) $path->Region;
        $result['District'] = (string) $path->District;
        $result['Settlement'] = (string) $path->Settlement;

        if (isset($path->ParcelLocation->Urban)) {
            // У межах населеного пункту
            $result['ParcelLocation'] = "Urban";
        } elseif (isset($path->ParcelLocation->Rural)) {
            // За межами населеного пункту
            $result['ParcelLocation'] = "Rural";
        }

        if (!empty($path->ParcelAddress)) {
            $result['StreetType'] = (string) $path->ParcelAddress->StreetType;
            $result['StreetName'] = (string) $path->ParcelAddress->StreetName;
            $result['Building'] = (string) $path->ParcelAddress->Building;
            $result['Block'] = (string) $path->ParcelAddress->Block;

            if (!empty($result['StreetType']) && !empty($result['StreetName'])) {
                $street = $result['StreetType'] . " " . $result['StreetName'];
            } else {
                $street = $result['StreetName'];
            }

            $result['FullAddress'] = $this->arrayImplode(", ", array($result['Region'], $result['District'], $result['Settlement'], $street, $result['Building'], $result['Block']));
        } else {
            $result['FullAddress'] = $this->arrayImplode(", ", array($result['Region'], $result['District'], $result['Settlement']));
        }

        $result = array_merge($result, $this->getAdditionalInfoBlock($path->AdditionalInfoBlock));

        return array('ParcelLocationInfo' => $result);
    }

    /**
     * Метрична інформація земельної ділянки, обмежень її використання та угідь
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelMetricInfo(SimpleXMLElement $path) {
        $result = array();

        $result['ParcelID'] = (string) $path->ParcelID;
        $result['Area']['MeasurementUnit'] = (string) $path->Area->MeasurementUnit;
        $result['Area']['Size'] = (string) $path->Area->Size;

        return array('ParcelMetricInfo' => $result);
    }

    /**
     * Блок опису усіх власників або користувачів земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelProprietors(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $proprietors_count = $path->ProprietorInfo->count();
            $get_from_proprietor = true;

            if ($proprietors_count == 1) {
                if (isset($path->ProprietorInfo->Authentication->NaturalPerson)) {
                    if (!$this->isValidNaturalPerson($path->ProprietorInfo->Authentication->NaturalPerson)) {
                        $get_from_proprietor = false;
                    }
                } elseif (isset($path->ProprietorInfo->Authentication->LegalEntity)) {
                    if (!$this->isValidLegalEntity($path->ProprietorInfo->Authentication->LegalEntity)) {
                        $get_from_proprietor = false;
                    }
                } else {
                    $get_from_proprietor = false;
                }
            }

            if ($get_from_proprietor) {
                foreach ($path->ProprietorInfo as $proprietor) {

                    $current_proprietor = array();

                    if (isset($proprietor->ParcelPart->Percent)) {
                        $current_proprietor['ParcelPart'] = (string) $proprietor->ParcelPart->Percent;
                    } elseif (isset($proprietor->ParcelPart->Part->Numerator) && isset($proprietor->ParcelPart->Part->Denominator)) {
                        $numerator = intval($proprietor->ParcelPart->Part->Numerator);
                        $denominator = intval($proprietor->ParcelPart->Part->Denominator);
                        if (preg_match("/^[0-9]+$/u", $numerator) && preg_match("/^[0-9]+$/u", $denominator)) {
                            $x = $numerator / $denominator;
                            if ($x < 1.0)
                                $current_proprietor['ParcelPart'] = (string) round($x * 100, 2);
                        }
                    }

                    if (isset($proprietor->Authentication->NaturalPerson)) {
                        $current_proprietor['Type'] = 1;
                        $current_proprietor['NaturalPerson'] = $this->getNaturalPersonType($proprietor->Authentication->NaturalPerson);
                    } else {
                        $current_proprietor['Type'] = 2;
                        $current_proprietor['LegalEntity'] = $this->getLegalEntityType($proprietor->Authentication->LegalEntity);
                    }

                    $current_proprietor['ProprietorCode'] = (string) $proprietor->ProprietorCode;

                    // Дані про пільги
                    if (!empty($proprietor->Privilege)) {
                        $privilege_info = array();

                        foreach ($proprietor->Privilege as $privilege) {
                            if (!empty($privilege->RegistrationInfo)) {
                                $current_privilege = array();

                                $current_privilege['RegName'] = (string) $privilege->RegistrationInfo->RegName;
                                $current_privilege = array_merge($current_privilege, $this->makeDate($privilege->RegistrationInfo->RegistrationDate, 'RegistrationDate', "d.m.Y"));

                                $privilege_info[] = $current_privilege;
                            }
                        }

                        if (count($privilege_info)) {
                            $current_proprietor['Privilege'] = $privilege_info;
                        }
                    }

                    // В елементі <AdditionalInfo> зазначається точна назва документу, який є підставою набуття права власності на земельну ділянку
                    if (isset($proprietor->AdditionalInfoBlock->AdditionalInfo)) {
                        $current_proprietor['AdditionalInfo'] = (string) $proprietor->AdditionalInfoBlock->AdditionalInfo;
                    }

                    // Підстава набуття права власності
                    if (!empty($proprietor->PropertyAcquisitionJustification)) {
                        $current_proprietor['PropertyAcquisitionJustification']['Document'] = (string) $proprietor->PropertyAcquisitionJustification->Document;
                        $current_proprietor['PropertyAcquisitionJustification'] = array_merge($current_proprietor['PropertyAcquisitionJustification'], $this->makeDate($proprietor->PropertyAcquisitionJustification->DocumentDate, 'DocumentDate', "d.m.Y"));
                        $current_proprietor['PropertyAcquisitionJustification']['DocumentNumber'] = (string) $proprietor->PropertyAcquisitionJustification->DocumentNumber;
                        $current_proprietor['PropertyAcquisitionJustification']['ApprovalAuthority'] = (string) $proprietor->PropertyAcquisitionJustification->ApprovalAuthority;
                    }

                    $result[] = $current_proprietor;
                }
            } else {
                $result = $this->getParcelProprietorsFromLegalModeInfo();
            }
        } else {
            $result = $this->getParcelProprietorsFromLegalModeInfo();
        }

        return array('Proprietors' => $result);
    }

    private function getParcelProprietorsFromLegalModeInfo() {
        $result = array();

        // LegalModeType = 1
        $grantors = $this->getGrantors(array(1));

        if (count($grantors)) {
            foreach ($grantors as $grantor) {
                $current_grantor = array();

                if (isset($grantor->NaturalPerson)) {
                    $current_grantor['Type'] = 1;
                    $current_grantor['NaturalPerson'] = $this->getNaturalPersonType($grantor->NaturalPerson);
                } else {
                    $current_grantor['Type'] = 2;
                    $current_grantor['LegalEntity'] = $this->getLegalEntityType($grantor->LegalEntity);
                }

                $result[] = $current_grantor;
            }
        } else {
            // LegalModeType = 2
            $grantors = $this->getGrantors(array(2));
            $current_grantor = array();
            $grantor = $grantors[0];

            if (isset($grantor->NaturalPerson)) {
                $current_grantor['Type'] = 1;
                $current_grantor['NaturalPerson'] = $this->getNaturalPersonType($grantor->NaturalPerson);
            } else {
                $current_grantor['Type'] = 2;
                $current_grantor['LegalEntity'] = $this->getLegalEntityType($grantor->LegalEntity);
            }

            $result[] = $current_grantor;
        }

        return $result;
    }

    /**
     * Земельний сервітут, емфітевзис, суперфіцій та інші обмеження щодо користування земельною ділянкою
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelRestrictions(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $restrictions_info = array();

            foreach ($path->RestrictionInfo as $restriction) {
                $current_restriction = array();

                $current_restriction['RestrictionCode'] = (string) $restriction->RestrictionCode;
                $current_restriction['RestrictionName'] = (string) $restriction->RestrictionName;

                if (!empty($restriction->RestrictionEntitlement)) {
                    $current_restriction['RestrictionEntitlement']['DocumentType'] = (string) $restriction->RestrictionEntitlement->DocumentType;
                    $current_restriction['RestrictionEntitlement']['DocumentName'] = (string) $restriction->RestrictionEntitlement->DocumentName;
                    $current_restriction['RestrictionEntitlement'] = array_merge($current_restriction['RestrictionEntitlement'], $this->makeDate($restriction->RestrictionEntitlement, 'DocumentDate', "d.m.Y"));
                    $current_restriction['RestrictionEntitlement']['NaturalPerson'] = $this->getNaturalPersonType($restriction->RestrictionEntitlement->NaturalPerson);
                    $current_restriction['RestrictionEntitlement']['LegalEntity'] = $this->getNaturalPersonType($restriction->RestrictionEntitlement->LegalEntity);
                }

                if (!empty($restriction->RestrictionTerm)) {
                    if (isset($restriction->RestrictionTerm->Permanent)) {
                        $current_restriction['RestrictionTerm']['Permanent'] = (string) $restriction->RestrictionTerm->Permanent;
                    } elseif (isset($restriction->RestrictionTerm->Time)) {
                        $current_restriction['RestrictionTerm']['Time']['Duration'] = (string) $restriction->RestrictionTerm->Time->Duration;
                        $current_restriction['RestrictionTerm']['Time'] = array_merge($current_restriction['RestrictionTerm']['Time'], $this->makeDate($restriction->RestrictionTerm->Time->StartDate, 'StartDate', "d.m.Y"));
                        $current_restriction['RestrictionTerm']['Time'] = array_merge($current_restriction['RestrictionTerm']['Time'], $this->makeDate($restriction->RestrictionTerm->Time->ExpirationDate, 'ExpirationDate', "d.m.Y"));
                    }
                }

                $current_restriction = array_merge($current_restriction, $this->makeDate($restriction->RegistrationDate, 'RegistrationDate', "d.m.Y"));
                $current_restriction['RegistrationNumber'] = (string) $restriction->RegistrationNumber;

                if (isset($restriction->Beneficiaries->Beneficiary)) {
                    $beneficiaries = array();
                    foreach ($restriction->Beneficiaries->Beneficiary as $beneficiary) {
                        $current_beneficiary = array();

                        if (isset($beneficiary->NaturalPerson)) {
                            $current_beneficiary['NaturalPerson'] = $this->getNaturalPersonType($beneficiary->NaturalPerson);
                        } elseif (isset($beneficiary->LegalEntity)) {
                            $current_beneficiary['LegalEntity'] = $this->getNaturalPersonType($beneficiary->LegalEntity);
                        }

                        $beneficiaries[] = $current_beneficiary;
                    }

                    if (count($beneficiaries)) {
                        $current_restriction['Beneficiaries'] = $beneficiaries;
                    }
                }

                if (!empty($restriction->Payment)) {
                    if (isset($restriction->Payment->Free)) {
                        $current_restriction['Payment']['Free'] = (string) $restriction->Payment->Free;
                    } elseif (isset($restriction->Payment->Paid)) {
                        $current_restriction['Payment']['Paid']['MoneyPayment'] = (string) $restriction->Payment->Paid->MoneyPayment;
                        $current_restriction['Payment']['Paid']['OtherPayment'] = (string) $restriction->Payment->Paid->OtherPayment;
                    }
                }

                $restrictions_info[] = $current_restriction;
            }

            if (count($restrictions_info)) {
                $result = array('Restrictions' => $restrictions_info);
            }
        }

        return $result;
    }

    /**
     * Державний акт на земельну ділянку
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelStateActInfo(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $state_act_info = array();
            foreach ($path as $state_act) {
                $current_state_act = array();

                $current_state_act['StateActType'] = (string) $state_act->StateActType;

                if (isset($state_act->StateActForm->Series)) {
                    $current_state_act['StateActForm']['Series'] = (string) $state_act->StateActForm->Series;
                }

                if (isset($state_act->StateActForm->Number)) {
                    $current_state_act['StateActForm']['Number'] = (string) $state_act->StateActForm->Number;
                }

                // Реєстрація державного акта
                if (!empty($state_act->StateActRegistrationInfo)) {
                    $current_state_act['StateActRegistrationInfo']['RegistrationBookNumber'] = (string) $state_act->StateActRegistrationInfo->RegistrationBookNumber;
                    $current_state_act['StateActRegistrationInfo']['SectionNumber'] = (string) $state_act->StateActRegistrationInfo->SectionNumber;
                    $current_state_act['StateActRegistrationInfo']['RegistrationNumber'] = (string) $state_act->StateActRegistrationInfo->RegistrationNumber;
                    $current_state_act['StateActRegistrationInfo'] = array_merge($current_state_act['StateActRegistrationInfo'], $this->makeDate($state_act->StateActRegistrationInfo->RegistrationDate, 'RegistrationDate', "d.m.Y"));

                    if (!empty($state_act->StateActRegistrationInfo->RegistrationPersonInfo)) {

                        foreach ($state_act->StateActRegistrationInfo->RegistrationPersonInfo->SignedByName as $value) {
                            $current_state_act['StateActRegistrationInfo']['RegistrationPersonInfo']['SignedByName'][] = $this->getFullNameType($value);
                        }

                        foreach ($state_act->StateActRegistrationInfo->RegistrationPersonInfo->SignedByPosition as $value) {
                            $current_state_act['StateActRegistrationInfo']['RegistrationPersonInfo']['SignedByPosition'][] = (string) $value;
                        }

                        if (!empty($state_act->StateActRegistrationInfo->RegistrationPersonInfo->RegisteredBy)) {
                            $current_state_act['StateActRegistrationInfo']['RegistrationPersonInfo']['RegisteredBy'] = $this->getFullNameType($state_act->StateActRegistrationInfo->RegistrationPersonInfo->RegisteredBy);
                        }

                        if (!empty($state_act->StateActRegistrationInfo->RegistrationPersonInfo->RegisteredByPosition)) {
                            $current_state_act['StateActRegistrationInfo']['RegistrationPersonInfo']['RegisteredByPosition'] = (string) $state_act->StateActRegistrationInfo->RegistrationPersonInfo->RegisteredByPosition;
                        }
                    }
                }

                $current_state_act = array_merge($current_state_act, $this->makeDate($state_act->DeliveryDate, 'DeliveryDate', "d.m.Y"));

                if (isset($state_act->DeliveryPerson->NaturalPerson)) {
                    $current_state_act['DeliveryPerson']['NaturalPerson'] = $this->getNaturalPersonType($state_act->DeliveryPerson->NaturalPerson);
                } elseif (isset($state_act->DeliveryPerson->LegalEntity)) {
                    $current_state_act['DeliveryPerson']['LegalEntity'] = $this->getLegalEntityType($state_act->DeliveryPerson->LegalEntity);
                }

                if (!empty($state_act->EntitlementDocument)) {
                    $current_state_act['EntitlementDocument']['Document'] = (string) $state_act->EntitlementDocument->Document;
                    $current_state_act['EntitlementDocument'] = array_merge($current_state_act['EntitlementDocument'], $this->makeDate($state_act->EntitlementDocument->DocumentDate, 'DocumentDate', "d.m.Y"));
                    $current_state_act['EntitlementDocument']['DocumentNumber'] = (string) $state_act->EntitlementDocument->DocumentNumber;
                    $current_state_act['EntitlementDocument']['ApprovalAuthority'] = (string) $state_act->EntitlementDocument->ApprovalAuthority;
                }

                if (!empty($state_act->MarkInfo)) {
                    $mark_info = array();

                    foreach ($state_act->MarkInfo as $mark) {
                        $current_mark = array();

                        if (!empty($mark->NotaryMark)) {
                            if (isset($mark->NotaryMark->NotaryName->LegalEntity)) {
                                $current_mark['NotaryMark']['NotaryName']['LegalEntity'] = $this->getLegalEntityType($mark->NotaryMark->NotaryName->LegalEntityInfo);
                            }

                            if (isset($mark->NotaryMark->NotaryName->FullName)) {
                                $current_mark['NotaryMark']['NotaryName']['FullName'] = $this->getFullNameType($mark->NotaryMark->NotaryName->FullName);
                            }

                            $current_mark['NotaryMark'] = array_merge($current_mark['NotaryMark'], $this->makeDate($mark->NotaryMark->NotaryMarkDate, 'NotaryMarkDate', "d.m.Y"));
                            $current_mark['NotaryMark']['NotaryMarkNumber'] = (string) $mark->NotaryMark->NotaryMarkNumber;
                        }

                        if (!empty($mark->RegistrationAuthorityMark)) {
                            $current_mark['RegistrationAuthorityMark']['RegistrationAuthorityName'] = (string) $mark->RegistrationAuthorityMark->RegistrationAuthorityName;
                            $current_mark['RegistrationAuthorityMark'] = array_merge($current_mark['RegistrationAuthorityMark'], $this->makeDate($mark->RegistrationAuthorityMark->RegistrationAuthorityDate, 'RegistrationAuthorityDate', "d.m.Y"));
                            $current_mark['RegistrationAuthorityMark']['RegistrationAuthorityNumber'] = (string) $mark->RegistrationAuthorityMark->RegistrationAuthorityNumber;

                            if (isset($mark->RegistrationAuthorityMark->OwnerName->NaturalPerson)) {
                                $current_mark['RegistrationAuthorityMark']['OwnerName']['NaturalPerson'] = $this->getNaturalPersonType($mark->RegistrationAuthorityMark->OwnerName->NaturalPerson);
                            } elseif ($mark->RegistrationAuthorityMark->OwnerName->LegalEntity) {
                                $current_mark['RegistrationAuthorityMark']['OwnerName']['LegalEntity'] = $this->getLegalEntityType($mark->RegistrationAuthorityMark->OwnerName->LegalEntity);
                            }
                        }

                        if (count($current_mark)) {
                            $mark_info[] = $current_mark;
                        }
                    }

                    if (count($mark_info)) {
                        $current_state_act['MarkInfo'] = $mark_info;
                    }
                }

                $state_act_info[] = $current_state_act;
            }

            if (count($state_act_info)) {
                $result = array('StateActInfo' => $state_act_info);
            }
        }

        return $result;
    }

    /**
     * Суборенда земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    public function getParcelSubleases(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $subleases_info = array();
            $break = false;

            foreach ($path->SubleaseInfo as $sublease) {
                $current_sublease = array();

                if (isset($sublease->Subleasees->Subleasee)) {
                    if ($sublease->Subleasees->Subleasee->count() > 1) {
                        foreach ($sublease->Subleasees->Subleasee as $subleasee) {
                            if (isset($subleasee->NaturalPerson)) {
                                if ($this->isValidNaturalPerson($subleasee->NaturalPerson))
                                    $current_sublease['Subleasee'][]['NaturalPerson'] = $this->getNaturalPersonType($subleasee->NaturalPerson);
                            } elseif (isset($subleasee->LegalEntity)) {
                                if ($this->isValidLegalEntity($subleasee->LegalEntity))
                                    $current_sublease['Subleasee'][]['LegalEntity'] = $this->getLegalEntityType($subleasee->LegalEntity);
                            }
                        }
                    } else {
                        $get_from_grantee = false;

                        if (isset($sublease->Subleasees->Subleasee->NaturalPerson)) {
                            if ($this->isValidNaturalPerson($sublease->Subleasees->Subleasee->NaturalPerson)) {
                                $current_sublease['Subleasee'][]['NaturalPerson'] = $this->getNaturalPersonType($sublease->Subleasees->Subleasee->NaturalPerson);
                            } else {
                                $get_from_grantee = true;
                            }
                        } elseif (isset($sublease->Subleasees->Subleasee->LegalEntity)) {
                            if ($this->isValidLegalEntity($sublease->Subleasees->Subleasee->LegalEntity)) {
                                $current_sublease['Subleasee'][]['LegalEntity'] = $this->getLegalEntityType($sublease->Subleasees->Subleasee->LegalEntity);
                            } else {
                                $get_from_grantee = true;
                            }
                        }

                        if ($get_from_grantee) {
                            $grantee = $this->getGrantee(3);

                            foreach ($grantee as $value) {
                                if (isset($value->NaturalPerson)) {
                                    if ($this->isValidNaturalPerson($value->NaturalPerson))
                                        $current_sublease['Subleasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                                } elseif (isset($value->LegalEntity)) {
                                    if ($this->isValidLegalEntity($value->LegalEntity))
                                        $current_sublease['Subleasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                                }
                            }
                        }
                    }
                } else {
                    $grantee = $this->getGrantee(3);

                    foreach ($grantee as $value) {
                        if (isset($value->NaturalPerson)) {
                            if ($this->isValidNaturalPerson($value->NaturalPerson))
                                $current_sublease['Subleasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                        } elseif (isset($value->LegalEntity)) {
                            if ($this->isValidLegalEntity($value->LegalEntity))
                                $current_sublease['Subleasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                        }
                    }

                    $break = true;
                }

                $current_sublease['Area'] = (string) $sublease->Area;
                $current_sublease = array_merge($current_sublease, $this->makeDate($sublease->RegistrationDate, 'RegistrationDate', "d.m.Y"));
                $current_sublease['RegistrationNumber'] = (string) $sublease->RegistrationNumber;

                if (!empty($sublease->Subrent)) {
                    if (!empty($sublease->Subrent->MoneySubrent)) {
                        $current_sublease['Subrent']['MoneySubrent'] = (string) $sublease->Subrent->MoneySubrent;
                    } elseif (!empty($sublease->Subrent->OtherSubrent)) {
                        $current_sublease['Subrent']['OtherSubrent'] = (string) $sublease->Subrent->OtherSubrent;
                    }
                }

                if (!empty($sublease->SubleaseTerm)) {
                    $current_sublease['SubleaseTerm']['SubleaseDuration'] = (string) $sublease->SubleaseTerm->SubleaseDuration;
                    $current_sublease['SubleaseTerm'] = array_merge($current_sublease['SubleaseTerm'], $this->makeDate($sublease->SubleaseTerm->StartDate, 'StartDate', "d.m.Y"));
                    $current_sublease['SubleaseTerm'] = array_merge($current_sublease['SubleaseTerm'], $this->makeDate($sublease->SubleaseTerm->ExpirationDate, 'ExpirationDate', "d.m.Y"));
                }

                if (!empty($current_sublease))
                    $subleases_info[] = $current_sublease;

                if ($break)
                    break;
            }

            if (count($subleases_info))
                $result = array('Subleases' => $subleases_info);
        } else {
            $subleases_info = array();
            $grantee = $this->getGrantee(3);

            if (count($grantee)) {
                foreach ($grantee as $value) {
                    $current_sublease = array();

                    if (isset($value->NaturalPerson)) {
                        if ($this->isValidNaturalPerson($value->NaturalPerson))
                            $current_sublease['Subleasee'][]['NaturalPerson'] = $this->getNaturalPersonType($value->NaturalPerson);
                    } elseif (isset($value->LegalEntity)) {
                        if ($this->isValidLegalEntity($value->LegalEntity))
                            $current_sublease['Subleasee'][]['LegalEntity'] = $this->getLegalEntityType($value->LegalEntity);
                    }

                    if (!empty($current_sublease))
                        $subleases_info[] = $current_sublease;
                }
            }

            if (count($subleases_info)) {
                $result = array('Subleases' => $subleases_info);
            }
        }

        return $result;
    }

    /**
     * Реквізити технічної документації
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelTechnicalDocumentation(SimpleXMLElement $path) {
        $result = array();

        $result['DocumentationType'] = (string) $path->DocumentationType;
        $result = array_merge($result, $this->makeDate($path->DraftingDate, 'DraftingDate', "d.m.Y"));

        if (!empty($path->RegistrationData)) {
            $result['RegistrationData']['BookNumber'] = (string) $path->RegistrationData->BookNumber;
            $result['RegistrationData'] = array_merge($result['RegistrationData'], $this->makeDate($path->RegistrationData->RegistrationDate, 'RegistrationDate', "d.m.Y"));
            $result['RegistrationData']['RegistrationAuthority'] = (string) $path->RegistrationData->RegistrationAuthority;
        }

        if (!empty($path->RegistrationCard)) {
            $registration_cards = array();
            foreach ($path->RegistrationCard as $card) {
                $current_card = array();
                $current_card['BookNumber'] = (string) $card->BookNumber;
                $current_card = array_merge($current_card, $this->makeDate($card->IssuanceDate, 'IssuanceDate', "d.m.Y"));

                $registration_cards[] = $current_card;
            }

            if (count($registration_cards)) {
                $result['RegistrationCard'] = $registration_cards;
            }
        }

        if (!empty($path->DocumentList)) {
            foreach ($path->DocumentList as $document) {
                $result['DocumentList'][] = (string) $document;
            }
        }

        if (!empty($path->Expertise)) {
            $result['Expertise']['ExperiseRequired'] = (string) $path->Expertise->ExpertiseRequired;
            $result['Expertise']['ExpertiseАuthority'] = (string) $path->Expertise->ExpertiseАuthority;
            $result['Expertise']['ExpertOpinion'] = (string) $path->Expertise->ExpertOpinion;
            $result['Expertise'] = array_merge($result['Expertise'], $this->makeDate($path->Expertise->ExpertiseDate, 'ExpertiseDate', "d.m.Y"));
            $result['Expertise']['ExpertiseNumber'] = (string) $path->Expertise->ExpertiseNumber;
        }

        if (!empty($path->ApprovalInfo)) {
            $result['ApprovalInfo']['ApprovalAuthority'] = (string) $path->ApprovalInfo->ApprovalAuthority;
            $result['ApprovalInfo'] = array_merge($result['ApprovalInfo'], $this->makeDate($path->ApprovalInfo->ApprovalDate, 'ApprovalDate', "d.m.Y"));
            $result['ApprovalInfo']['ApprovalNumber'] = (string) $path->ApprovalInfo->ApprovalNumber;
            $result['ApprovalInfo']['ApprovalDocument'] = (string) $path->ApprovalInfo->ApprovalDocument;
        }

        if (!empty($path->ApprovalActDate)) {
            $result = array_merge($result, $this->makeDate($path->ApprovalActDate, 'ApprovalActDate', "d.m.Y"));
        }

        return array('TechnicalDocumentationInfo' => $result);
    }

    /**
     * Грошова оцінка земельної ділянки
     * @param SimpleXMLElement $path
     * @return array
     */
    private function getParcelValuationInfo(SimpleXMLElement $path) {
        $result = array();

        if (!empty($path)) {
            $valuation_info = array();

            foreach ($path as $valuation) {
                if (!empty($valuation->Normative)) {
                    $current_valuation = array();

                    $current_valuation = array_merge($current_valuation, $this->makeDate($valuation->Normative->ValuationDate, 'ValuationDate', "d.m.Y"));
                    $current_valuation['Value'] = (string) $valuation->Normative->Value;
                    $current_valuation['Executor'] = array_merge($current_valuation, $this->getExecutorType($valuation->Normative->Executor));

                    if (isset($valuation->Normative->ValuationDocumentation)) {
                        $current_valuation['ValuationDocumentation']['Number'] = (string) $valuation->Normative->ValuationDocumentation->Number;
                        $current_valuation['ValuationDocumentation']['Attribute'] = (string) $valuation->Normative->ValuationDocumentation->Attribute;
                    }

                    $valuation_info[] = $current_valuation;
                }
            }

            if (count($valuation_info)) {
                $result = array('ValuationInfo' => $valuation_info);
            }
        }

        return $result;
    }

    /**
     * Службова інформація
     * @return array
     */
    public function getServiceInfo() {
        $result = array();

        if (!$this->hasErrors()) {
            $service_info = $this->_simple_xml_object->AdditionalPart->ServiceInfo;

            if (!empty($service_info->FileID)) {
                $result = array_merge($result, $this->makeDate($service_info->FileID->FileDate, 'FileDate', "d.m.Y"));
                $result['FileGUID'] = (string) $service_info->FileID->FileGUID;
            }

            $result['FormatVersion'] = (string) $service_info->FormatVersion;
            $result['ReceiverName'] = (string) $service_info->ReceiverName;

            if (!empty($service_info->ReceiverIdentifier)) {
                $result['ReceiverIdentifier'] = (string) $service_info->ReceiverIdentifier;
            }

            $result['Software'] = (string) $service_info->Software;
            $result['SoftwareVersion'] = (string) $service_info->SoftwareVersion;
        }

        return $result;
    }

    /**
     * Загружает обменный файл из строки
     * @param string $filebody
     */
    public function loadFromString($filebody) {
        $this->_file_body = $filebody;
        $this->_simple_xml_object = @simplexml_load_string($this->_file_body);
        if ($this->_simple_xml_object) {
            $this->validate();
        } else {
            $this->logError("Файл", "Невірний формат даних", "", "Критична");
        }
    }

    /**
     *
     * @param SimpleXMLElement $path
     * @param string $field_name
     * @return array
     */
    private function makeDate(SimpleXMLElement $path, $name, $format) {
        $result = array();
        $str_date = null;

        if (!empty($path)) {
            $date = (string) $path;
            if (preg_match("/^[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])$/u", $date)) {
                $date = new DateTime($date);
                $str_date = $date->format($format);
            }
        }

        $result[$name] = $str_date;

        return $result;
    }

    /**
     * Перевірка на помилки
     */
    private function validate() {
        $this->validateCadastralZone();
    }

    /**
     * Перевірка кадастрового кварталу на помилки
     */
    private function validateCadastralQuarter() {
        if (isset($this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo)) {
            $cad_quarter = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo;

            // Номер кадастрового кварталу
            if (!$this->_options['ignore_cad_num']) {
                if (!empty($cad_quarter->CadastralQuarterNumber)) {
                    if (!preg_match("/^[0-9]{3}$/u", (string) $cad_quarter->CadastralQuarterNumber)) {
                        $this->logError("CadastralQuarterNumber", "Номер кадастрового кварталу (тризначний)", (string) $cad_quarter->CadastralQuarterNumber, "Невірний формат елементу");
                    }
                } else {
                    $this->logError("CadastralQuarterNumber", "Номер кадастрового кварталу (тризначний)", "", "Значення елементу відсунє");
                }
            }

            // Блок опису земельних ділянок
            if (!empty($cad_quarter->Parcels)) {
                $this->validateParcel();
            } else {
                $this->logError("Parcels", "Блок опису земельних ділянок", "", "Значення елементу відсунє");
            }
        } else {
            $this->logError("CadastralQuarterInfo", "Кадастровий квартал", "", "Значення елементу відсунє");
        }
    }

    /**
     * Перевірка кадастрової зони на помилки
     */
    private function validateCadastralZone() {
        if (isset($this->_simple_xml_object->InfoPart->CadastralZoneInfo)) {
            $cad_zone = $this->_simple_xml_object->InfoPart->CadastralZoneInfo;

            if (!$this->_options['ignore_cad_num']) {
                // КОАТУУ
                if (!empty($cad_zone->KOATUU)) {
                    if (!preg_match("/^[0-9]{10}$/u", (string) $cad_zone->KOATUU)) {
                        $this->logError("KOATUU", "Код об’єкта адміністративно-територіального устрою України (десятизначний)", (string) $cad_zone->KOATUU, "Невірний формат елементу");
                    }
                } else {
                    $this->logError("KOATUU", "Код об’єкта адміністративно-територіального устрою України (десятизначний)", "", "Значення елементу відсунє");
                }

                // Номер кадастрової зони
                if (!empty($cad_zone->CadastralZoneNumber)) {
                    if (!preg_match("/^[0-9]{2}$/u", (string) $cad_zone->CadastralZoneNumber)) {
                        $this->logError("CadastralZoneNumber", "Номер кадастрової зони (двозначний)", (string) $cad_zone->CadastralZoneNumber, "Невірний формат елементу");
                    }
                } else {
                    $this->logError("CadastralZoneNumber", "Номер кадастрової зони (двозначний)", "", "Значення елементу відсунє");
                }
            }

            // Блок опису кадастрових кварталів
            if (!empty($cad_zone->CadastralQuarters)) {
                $this->validateCadastralQuarter();
            } else {
                $this->logError("CadastralQuarters", "Блок опису кадастрових кварталів", "", "Значення елементу відсунє");
            }
        } else {
            $this->logError("CadastralZoneInfo", "Кадастрова зона", "", "Значення елементу відсунє");
        }
    }

    /**
     * Перевірка земельної ділянки на помилки
     */
    private function validateParcel() {
        if (isset($this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo)) {
            $parcel = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo;

            // Місцезнаходження земельної ділянки
            if (!empty($parcel->ParcelLocationInfo)) {
                $this->validateParcelLocation();
            } else {
                $this->logError("ParcelLocationInfo ", "Місцезнаходження земельної ділянки", "", "Значення елементу відсунє");
            }

            // Категорія та цільове призначення (використання) земельної ділянки
            if (!empty($parcel->CategoryPurposeInfo)) {
                $this->validateParcelCategoryPurpose();
            } else {
                $this->logError("CategoryPurposeInfo", "Категорія та цільове призначення (використання) земельної ділянки", "", "Значення елементу відсунє");
            }

            // Форма власності на земельну ділянку
            if (!empty($parcel->OwnershipInfo)) {
                $this->validateParcelOwnership();
            } else {
                $this->logError("OwnershipInfo ", "Форма власності на земельну ділянку ", "", "Значення елементу відсунє");
            }

            // Метрична інформація земельної ділянки, обмежень її використання та угідь
            if (!empty($parcel->ParcelMetricInfo)) {
                $this->validateParcelMetric();
            } else {
                $this->logError("ParcelMetricInfo", "Метрична інформація земельної ділянки, обмежень її використання та угідь", "", "Значення елементу відсунє");
            }

            // Реквізити документації
            if (!empty($parcel->TechnicalDocumentationInfo)) {
                $this->validateParcelTechDoc();
            } else {
                $this->logError("TechnicalDocumentationInfo", "Реквізити документації", "", "Значення елементу відсунє");
            }

            // Блок опису усіх власників або користувачів земельної ділянки
            $this->validateParcelOwners();
        } else {
            $this->logError("ParcelInfo", "Земельна ділянка", "", "Значення елементу відсунє");
        }
    }

    private function validateParcelCategoryPurpose() {
        $purpose_info = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->CategoryPurposeInfo;

        // Категорія земель
        if (!empty($purpose_info->Category)) {
            if (!preg_match("/^[0-9][0]{2}$/u", (string) $purpose_info->Category)) {
                $this->logError("Category", "Вид категорії земель за їх основним цільовим призначенням (використанням)", (string) $purpose_info->Category, "Невірний формат елементу");
            }
        } else {
            $this->logError("Category", "Вид категорії земель за їх основним цільовим призначенням (використанням)", "", "Значення елементу відсунє");
        }

        // Цільове призначення (використання) земельної ділянки
        if (empty($purpose_info->Purpose)) {
            $this->logError("Purpose", "Цільове призначення (використання) земельної ділянки (Згідно із документацією із землеустрою)", "", "Значення елементу відсунє");
        }
    }

    private function validateParcelLocation() {
        $location_info = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->ParcelLocationInfo;

        // Регіон
        if (empty($location_info->Region)) {
            $this->logError("Region", "Регіон", "", "Значення елементу відсунє");
        }
    }

    private function validateParcelMetric() {
        $metric_info = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->ParcelMetricInfo;

        if (!$this->_options['ignore_cad_num']) {
            if (!empty($metric_info->ParcelID)) {
                if (!preg_match("/^[0-9]{4}$/u", (string) $metric_info->ParcelID)) {
                    $this->logError("ParcelID", "Унікальний номер ділянки в межах кварталу (чотиризначний)", (string) $metric_info->ParcelID, "Невірний формат елементу");
                }
            } else {
                $this->logError("ParcelID", "Унікальний номер ділянки в межах кварталу (чотиризначний)", "", "Значення елементу відсунє");
            }
        }

        // Одиниці виміру площі
        if (isset($metric_info->Area->MeasurementUnit)) {
            if (!preg_match("/^(га\.?|кв.м\.?)$/u", (string) $metric_info->Area->MeasurementUnit)) {
                $this->logError("MeasurementUnit", "Одиниці виміру площі, в га або кв.м", (string) $metric_info->Area->MeasurementUnit, "Невірний формат елементу");
            }
        } else {
            $this->logError("MeasurementUnit", "Одиниці виміру площі, в га або кв.м", "", "Значення елементу відсунє");
        }

        // Площа земельної ділянки
        if (isset($metric_info->Area->Size)) {
            if (!preg_match("/^[\d]*\.[\d]*$/u", (string) $metric_info->Area->Size)) {
                $this->logError("Size", "Площа", (string) $metric_info->Area->Size, "Невірний формат елементу");
            }
        } else {
            $this->logError("Size", "Площа", "", "Значення елементу відсунє");
        }
    }

    private function validateParcelOwners() {
        if (isset($this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->Proprietors)) {
            $owners = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->Proprietors->ProprietorInfo;
            $find_owner = false;
            $validate_grantors = false;

            if ($owners->count()) {
                foreach ($owners as $proprietor) {
                    // Дані про фізичну особу
                    if (isset($proprietor->Authentication->NaturalPerson)) {
                        if ($this->isValidNaturalPerson($proprietor->Authentication->NaturalPerson)) {
                            $find_owner = true;
                        } else {
                            if ($find_owner || $owners->count() > 1) {
                                $this->validateNaturalPerson($proprietor->Authentication->NaturalPerson);
                            } else {
                                $grantors = $this->getGrantors(array(1, 2));
                                if (count($grantors) == 0) {
                                    $this->validateNaturalPerson($proprietor->Authentication->NaturalPerson);
                                } else {
                                    $validate_grantors = true;
                                }
                            }
                        }
                    }
                    // Дані про юридичну особу
                    elseif (isset($proprietor->Authentication->LegalEntity)) {
                        if ($this->isValidLegalEntity($proprietor->Authentication->LegalEntity)) {
                            $find_owner = true;
                        } else {
                            if ($find_owner || $owners->count() > 1) {
                                $this->validateLegalEntity($proprietor->Authentication->LegalEntity);
                            } else {
                                $grantors = $this->getGrantors(array(1, 2));
                                if (count($grantors) == 0) {
                                    $this->validateLegalEntity($proprietor->Authentication->LegalEntity);
                                } else {
                                    $validate_grantors = true;
                                }
                            }
                        }
                    } else {
                        $this->logError("Authentication", "Інформація про власника земельної ділянки", "", "Значення елементу відсунє");
                        break;
                    }
                }

                if ($validate_grantors) {
                    $this->validateGrantors();
                }
            } else {
                $this->logError("ProprietorInfo", "Інформація про власника земельної ділянки", "", "Значення елементу відсунє");
            }
        } else {
            $this->validateGrantors();
        }
    }

    private function validateGrantors() {
        $grantors = $this->getGrantors(array(1));

        if (count($grantors)) {
            foreach ($grantors as $grantor) {
                if (isset($grantor->NaturalPerson)) {
                    if (!$this->isValidNaturalPerson($grantor->NaturalPerson)) {
                        $this->validateNaturalPerson($grantor->NaturalPerson);
                    }
                } elseif (isset($grantor->LegalEntity)) {
                    if (!$this->isValidLegalEntity($grantor->LegalEntity)) {
                        $this->validateLegalEntity($grantor->LegalEntity);
                    }
                } else {
                    $this->logError("Grantor", "Інформація про власника земельної ділянки", "", "Значення елементу відсунє");
                }
            }
        } else {
            $grantors = $this->getGrantors(array(2));
            if (count($grantors)) {
                $grantor = $grantors[0];
                if (isset($grantor->NaturalPerson)) {
                    if (!$this->isValidNaturalPerson($grantor->NaturalPerson)) {
                        $this->validateNaturalPerson($grantor->NaturalPerson);
                    }
                } elseif (isset($grantor->LegalEntity)) {
                    if (!$this->isValidLegalEntity($grantor->LegalEntity)) {
                        $this->validateLegalEntity($grantor->LegalEntity);
                    }
                } else {
                    $this->logError("Grantor", "Інформація про власника земельної ділянки", "", "Значення елементу відсунє");
                }
            } else {
                $this->logError("Proprietors, LegalModeInfo", "Блоки опису усіх власників або користувачів земельної ділянки", "", "Значення блоків відсунє");
            }
        }
    }

    private function validateParcelOwnership() {
        $ownership_info = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->OwnershipInfo;

        // Форма власності на земельну ділянку
        if (!empty($ownership_info->Code)) {
            if (!preg_match("/^[1-3][0]{2}$/u", (string) $ownership_info->Code)) {
                $this->logError("Code", "Форма власності на земельну ділянку", (string) $ownership_info->Code, "Невірний формат елементу");
            }
        } else {
            $this->logError("Code", "Форма власності на земельну ділянку", "", "Значення елементу відсунє");
        }
    }

    private function validateParcelTechDoc() {
        $tech_doc = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->TechnicalDocumentationInfo;

        // Вид документації
        if (!empty($tech_doc->DocumentationType)) {
            if (!preg_match("/^[0]{1}[0-9]{2}$/u", (string) $tech_doc->DocumentationType)) {
                $this->logError("DocumentationType", "Вид документації із землеустрою (код)", (string) $tech_doc->DocumentationType, "Невірний формат елементу");
            }
        }

        // Дата складання документації
        if (!empty($tech_doc->DraftingDate)) {
            if (!preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u", (string) $tech_doc->DraftingDate)) {
                $this->logError("DraftingDate", "Дата складання документації", (string) $tech_doc->DraftingDate, "Невірний формат елементу");
            }
        }
    }

    private function isValidNaturalPerson(SimpleXMLElement $path) {
        if (!empty($path)) {
            if (empty($path->FullName->LastName) && empty($path->FullName->FirstName))
                return false;

            if (!empty($path->TaxNumber)) {
                if (!preg_match("/^[0-9]{10}$/u", (string) $path->TaxNumber))
                    return false;
            }

            if (isset($path->Passport->PassportNumber)) {
                if (!empty($path->Passport->PassportNumber)) {
                    if (!preg_match("/^[0-9]{1,10}$/u", (string) $path->Passport->PassportNumber))
                        return false;

                    if (!preg_match("/^[0]{1,10}$/u", (string) $path->Passport->PassportNumber)) {
                        if (empty($path->Passport->PassportSeries))
                            return false;
                    }
                }
            }

            return true;
        }

        return false;
    }

    private function validateNaturalPerson(SimpleXMLElement $path) {
        // Прізвище
        if (empty($path->FullName->LastName)) {
            $this->logError("LastName", "Прізвище власника земельної ділянки", "", "Значення елементу відсунє");
        }

        // Ім'я
        if (empty($path->FullName->FirstName)) {
            $this->logError("FirstName", "Ім'я власника земельної ділянки", "", "Значення елементу відсунє");
        }

        // Ідентифікаційний номер згідно з ДРФО (за наявності)
        if (!empty($path->TaxNumber)) {
            if (!preg_match("/^[0-9]{10}$/u", (string) $path->TaxNumber)) {
                $this->logError("TaxNumber", "Ідентифікаційний номер власника земельної ділянки згідно з ДРФО (за наявності)", (string) $path->TaxNumber, "Невірний формат елементу");
            }
        }

        // Номер документа
        if (isset($path->Passport->PassportNumber)) {
            if (!empty($path->Passport->PassportNumber)) {
                if (!preg_match("/^[0-9]{1,10}$/u", (string) $path->Passport->PassportNumber)) {
                    $this->logError("PassportNumber", "Номер документа власника земельної ділянки", (string) $path->Passport->PassportNumber, "Невірний формат елементу");
                }

                // Серія документа
                if (!preg_match("/^[0]{1,10}$/u", (string) $path->Passport->PassportNumber)) {
                    if (empty($path->Passport->PassportSeries)) {
                        $this->logError("PassportSeries", "Серія документа власника земельної ділянки", "", "Значення елементу відсунє");
                    }
                }
            }
        }
    }

    /**
     * Шукає блоки Grantor із типами власності $types
     * @param array $types Перелік типів власності
     * @return array Массив SimpleXMLElement
     */
    private function getGrantors(array $types) {
        $legal_mode_path = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->LegalModeInfo;
        $result = array();

        if (!empty($legal_mode_path)) {
            foreach ($legal_mode_path as $legal_mode) {
                if (preg_match("/^\d{1}$/u", (string) $legal_mode->LegalModeType)) {
                    $legal_mode_type = intval((string) $legal_mode->LegalModeType);

                    if (in_array($legal_mode_type, $types)) {
                        if (!empty($legal_mode->Grantor)) {
                            $result[] = $legal_mode->Grantor;
                        }
                    }
                }
            }
        }

        return $result;
    }

    /**
     * Шукає блоки Grantee із типом власності $type
     * @param integer $type Тип власності
     * @return array Массив SimpleXMLElement
     */
    private function getGrantee($type) {
        $legal_mode_path = $this->_simple_xml_object->InfoPart->CadastralZoneInfo->CadastralQuarters->CadastralQuarterInfo->Parcels->ParcelInfo->LegalModeInfo;
        $result = array();

        if (!empty($legal_mode_path)) {
            foreach ($legal_mode_path as $legal_mode) {
                if (preg_match("/^\d{1}$/u", (string) $legal_mode->LegalModeType)) {
                    $legal_mode_type = intval((string) $legal_mode->LegalModeType);

                    if ($legal_mode_type == $type) {
                        foreach ($legal_mode->Grantee as $grantee) {
                            $result[] = $grantee;
                        }
                    }
                }
            }
        }

        return $result;
    }

    private function isValidLegalEntity(SimpleXMLElement $path) {
        if (!empty($path)) {
            if (empty($path->Name))
                return false;

            if (isset($path->EDRPOU)) {
                if (!preg_match("/^[0-9]{8}$/u", (string) $path->EDRPOU))
                    return false;
            } else {
                return false;
            }

            return true;
        }

        return false;
    }

    private function validateLegalEntity(SimpleXMLElement $path) {
        // Найменування юридичної особи
        if (empty($path->Name)) {
            $this->logError("Name", "Найменування юридичної особи - власника земельної ділянки", "", "Значення елементу відсунє");
        }

        // Ідентифікаційний код згідно з ЄДРПОУ
        if (isset($path->EDRPOU)) {
            if (!preg_match("/^[0-9]{8}$/u", (string) $path->EDRPOU)) {
                $this->logError("EDRPOU", "Ідентифікаційний код юридичної особи згідно з ЄДРПОУ - власника земельної ділянки", (string) $path->EDRPOU, "Невірний формат елементу");
            }
        } else {
            $this->logError("EDRPOU", "Ідентифікаційний код юридичної особи згідно з ЄДРПОУ - власника земельної ділянки", "", "Значення елементу відсунє");
        }
    }

}

/**
 * ErrorLog
 */
class ErrorLogXml {

    protected $errors;

    function __construct() {
        $this->errors = array();
    }

    public function logError($tag, $description, $value, $error_description) {
        array_push($this->errors, array('tag' => $tag, 'description' => $description, 'value' => $value, 'error_description' => $error_description));
    }

    public function getErrors() {
        return $this->errors;
    }

    public function getErrorsCount() {
        return count($this->errors);
    }

    public function hasErrors() {
        if ($this->errors) {
            return true;
        }
        return false;
    }

}
